package org.example.service;

import org.apache.commons.beanutils.PropertyUtils;
import org.example.entity.SysMenu;
import org.example.mapper.SysMenuMapper;
import org.example.model.MenuModel;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class SysMenuService {

    @Resource
    private SysMenuMapper sysMenuMapper;

    public List<MenuModel> getRouterTree(String name) {
        // 查询对应的用户的所有的权限
        List<SysMenu> sysMenuList = sysMenuMapper.getSysMenuByUsername(name);
        /**
         * 根据用户拥有的所有的菜单权限去构建前端路由表：
         *   {
         *     path: '/sys',
         *     component: Layout,
         *     redirect: '/sys/user',
         *     name: 'SysManager',
         *     meta: { title: '系统管理', icon: 'el-icon-s-help' },
         *     children: [
         *       {
         *         path: 'user', // /sys/user
         *         name: 'UserManager',
         *         component: () => import('@/views/user/index'),
         *         meta: { title: '用户管理', icon: 'table' }
         *       },
         *       {
         *         path: 'dept',
         *         name: 'DepartmentManager',
         *         component: () => import('@/views/dept/index'),
         *         meta: { title: '部门管理', icon: 'tree' }
         *       }
         *     ]
         *   }
         */
        // TODO:  将List<SysMenu> 转换为 List<MenuModel>
        /**
         * 1.在书写递归结构的时候，先得到顶级结构，然后在挂后代元素；
         * 2.首先筛选出所有的顶级菜单；
         * 3.根据顶级菜单往下挂儿子菜单或者孙子菜单；
         * 4.filter(sm -> sm.getType == 0) 筛选出所有的顶级菜单；
         * 5.map() 将 SysMenu 转换为 MenuModel 对象
         * 6.筛选出所有的顶级菜单；
         */
//       List<MenuModel> menuModels = sysMenuList.stream().filter(sm -> sm.getType() == 0).map(sm -> convertSysMenuToModelModel(sm)).collect(Collectors.toList());
        List<MenuModel> rootMenuModels = sysMenuList.stream()
                .filter(sm -> sm.getType() == 0)
                .map(this::convertSysMenuToModelModel).collect(Collectors.toList());

        // 构建递归树， 据顶级菜单往下挂儿子菜单或者孙子菜单；
        generateRecursionRoutes(rootMenuModels, sysMenuList);

        return rootMenuModels;
    }

    /**
     * 构建递归路由信息
     * @param menuModels
     * @param sysMenuList
     */
    private void generateRecursionRoutes(List<MenuModel> menuModels, List<SysMenu> sysMenuList) {
        // null == menuModels || menuModels.isEmpty()
        if(!CollectionUtils.isEmpty(menuModels)) {
            // 循环获取每个路由，然后挂儿子路由: 儿子的parentId == 等于它的id
            menuModels.forEach(m -> {
                // 获取当前路由的id, 根据id找儿子(儿子的parentId为当前路由的id)
                Integer id = m.getId();
                /**
                 * Integer的值 [-128, 127]
                 * 1.filter(sm -> !sm.getType().equals(2)) 排除掉所有的按钮，就是所有的菜单 因为数据库中按钮的 type 为2
                 * 2.filter(sm -> sm.getParentId().equals(id)) 找到所有的儿子菜单
                 * 2.map(this::convertSysMenuToModelModel) 将找到的儿子转换 MenuModel
                 */
                List<MenuModel> children = sysMenuList.stream()
                        .filter(sm -> !sm.getType().equals(2))   // 筛选出所有的菜单
                        .filter(sm -> sm.getParentId().equals(id))  // 找到所有的子菜单
                        .map(this::convertSysMenuToModelModel).collect(Collectors.toList());

                /** */
                // 给菜单挂他们的按钮操作权限，因为很多菜单的操作权限是通过儿子的方式来挂的
                List<String> permission = sysMenuList.stream()
                        .filter(sm -> sm.getType().equals(2)) // 找到所有的按钮
                        .filter(sm -> sm.getParentId().equals(id))  // 在按钮中找到儿子
                        .map(sm -> sm.getPerms())  // 对于按钮的操作权限我们只需要 perms 这个权限数据即可
                        .collect(Collectors.toList());

                if(!CollectionUtils.isEmpty(permission)) {
                    m.getMeta().setPermission(permission);
                }

                if(!CollectionUtils.isEmpty(children)) {
                    // 将儿子路由塞进去
                    m.setChildren(children);
                    // 接着构建儿子的后代路由
                    generateRecursionRoutes(children, sysMenuList);
                }
            });
        }
    }
    /**
     * 1.将 SysMenu对象转换为 MenuModel对象
     * MenuModel将来对应的json:
     *   {
     *      path: 'dept',
     *      name: 'DepartmentManager',
     *      component: () => import('@/views/dept/index'),
     *      meta: { title: '部门管理', icon: 'tree' }
     *    }
     */
    private MenuModel convertSysMenuToModelModel(SysMenu sysMenu) {
        /**
         * PropertyUtils.copyProperties(); 将源对象中属性拷贝到目标对象中, 接受两个参数：
         *   1.第一个参数是目标对象；
         *   2.第二个参数是源对象；
         *  能够拷贝的前提是他们拥有者相同的属性
         */
        MenuModel.Meta meta = new MenuModel.Meta();
        MenuModel model = new MenuModel();
        try {
            PropertyUtils.copyProperties(meta, sysMenu);
            PropertyUtils.copyProperties(model, sysMenu);
            /**
             * 有一个类菜单很特殊：
             * 1. 这个菜单只有一个权限（菜单下只有一个权限的情况，数据就直接权限挂载菜单上），并且这个菜单的操作权限
             *   直接设计到菜单上了。参看数据库: "系统日志"
             * 2. 该菜单的类型是1; 这个权限就是菜单的 perms 本身，
             */
            if(1 == sysMenu.getType() && null != sysMenu.getPerms()) {
                meta.setPermission(Arrays.asList(sysMenu.getPerms()));
            }
            model.setMeta(meta);
            // 0表示顶级菜单，定义菜单的 component是 Layout; 儿子菜单的 component的值不是 “Layout”
            if(0 == sysMenu.getType()) {
                model.setComponent("Layout");
            }else {
                model.setComponent(sysMenu.getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return model;
    }
}
